﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WcfServer.Classes.Utility;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;

namespace WcfServer.Classes.Client
{
    public class DirectConnectionHandler
    {
        private Socket soc;
        private String SourceUserName;
        private String DestinationUserName;
        private String SourceIP;
        private String DestinationIP;
        private String DestinationPublicKeyXML;
        private byte[] SessionKey;
        private bool IsConnectionValid;
        public bool IsConnectionOpen { set; get; } // Use this to cancel the communication.

        public DirectConnectionHandler() { IsConnectionValid = false; IsConnectionOpen = false; }

        public DirectConnectionHandler(Socket s, String src_name, String dst_name, String src_ip, String dst_ip, String xml)// Generates SessionKey
        {
            soc = s;
            SourceUserName = src_name;
            DestinationUserName = dst_name;
            SourceIP = src_ip;
            DestinationIP = dst_ip;
            DestinationPublicKeyXML = xml;
            IsConnectionValid = false;
            IsConnectionOpen = false;
        }

        public void InitiateConnection()
        {
            DirectInitiator di = new DirectInitiator(SourceUserName);
            InitMsgWrapper msg = new InitMsgWrapper()
            {
                IsDirect = true,
                EncryptedMessageContent = di.Send(DestinationPublicKeyXML)
            };
            soc.Send(msg.Send());
            SessionKey = di.SessionKey;

            byte[] buffer = new byte[10000]; //TODO: is 10KB enough?
            int bytesReceived = soc.Receive(buffer);

            byte[] data = new byte[bytesReceived];
            Buffer.BlockCopy(buffer, 0, data, 0, bytesReceived);
            Ack ack = new Ack(data, SessionKey);
            //TODO: VALIDATION.
            Console.WriteLine("Acknowledged");
            IsConnectionValid = true;
        }

        public void ListenConnection()
        {
            byte[] buffer = new byte[10000]; //TODO: is 10KB enough?
            int bytesReceived = soc.Receive(buffer);

            byte[] data = new byte[bytesReceived];
            Buffer.BlockCopy(buffer, 0, data, 0, bytesReceived);

            InitMsgWrapper msg = new InitMsgWrapper(data);
            if (msg.IsDirect)
            {
                DirectInitiator di = new DirectInitiator(msg.EncryptedMessageContent, SourceUserName);//TODO: what should be use for container name? 
                //TODO: Check Sign Validity. Check Every Validation! Do we need an error handling?
                Ack ack = new Ack()
                {
                    TimeStamp = di.TimeStamp.AddSeconds(1),//T+1
                };

                byte[] ackToSend = ack.Send(di.SessionKey);// Ks ( T )
                soc.Send(ackToSend);

                SessionKey = di.SessionKey;
                IsConnectionValid = true;
            }
        }

        public void Communicate()
        {
            Task.Factory.StartNew(delegate
            {
               // try
                //{
                    while (IsConnectionValid && IsConnectionOpen)
                    {
                        byte[] buffer = new byte[10000];
                        int bytesReceived = soc.Receive(buffer); //blocking

                        byte[] data = new byte[bytesReceived];
                        Buffer.BlockCopy(buffer, 0, data, 0, bytesReceived);

                        PersonalMessage pm = new PersonalMessage(data, SessionKey);
                        //TODO: Validation required.
                        Console.WriteLine("{0} ({1}): {2}", pm.SenderUserID, pm.TimeStamp, pm.PMessage); //TODO: change them for use in UI.
                    }
                //}
                //catch (Exception e) { }
            });
            try
            {
                while (IsConnectionValid && IsConnectionOpen)
                {
                    PersonalMessage pm = new PersonalMessage()
                    {
                        PMessage = Console.ReadLine(),
                        SenderUserID = SourceUserName,
                        TimeStamp = DateTime.Now
                    };
                    soc.Send(pm.Send(SessionKey)); //TODO: will this work for both the thread above and this (using argument in delegate)? SOLVED by private property but the q itself?
                    Console.WriteLine("{0} ({1}): {2}", pm.SenderUserID, pm.TimeStamp, pm.PMessage);
                }
            }
            catch (Exception e) { }
        }
    }
}
